using System;
using System.IO;
using System.Collections.Generic;
using System.Configuration;
using System.Collections;
using System.Collections.Specialized;
using System.Diagnostics;

namespace TicketSellerService
{
	public class TicketsDB {
		private const int DATA_FIELDS_LENGTH = 6;
		
		private static  TicketsDB _instance = null;
		
		private IList<Flight> flights = new List<Flight>( );
		
		private IDictionary< int, List<TicketReservation>> reservations = new Dictionary< int, List<TicketReservation>>( );
		
		private Object locker = new Object( );
		
		private static Object initLock = new Object( );
		
		public enum Status { 
				RESERVED_TICKET, 
				RESERVATION_FAILED, 
				RESERVATION_CANCELLED, 
				CANCELATION_FAILED,
				FLIGHT_FULL
		};
		
		private TicketsDB ( ) {
			string fileName = ConfigurationManager.AppSettings[ "fileName"];
			StreamReader reader = null;
			string line = null;
			if ( fileName == null) {
				Console.WriteLine( "Flights file data couldn't be found.");
				return;
			}
			try {
				reader = File.OpenText( fileName);
				while ( ( line = reader.ReadLine( )) != null) {
					string[] data = line.Split( ' ');
					if ( data.Length != DATA_FIELDS_LENGTH) {
						Console.WriteLine( "Number of fields in the string is {0}", data.Length);
						Console.WriteLine( "Invalid input format.");
						continue;
					}
					
					Flight flight = new Flight( );
					try {
						flight.flightNumber = Convert.ToInt32( data[0]);
						flight.source = data[1];
						flight.destination = data[2];
						flight.date = Convert.ToDateTime( data[3]);
						flight.seatsCount = Convert.ToInt32( data[4]);
						flight.price = Convert.ToDouble( data[5]);
						flights.Add( flight);
					} catch ( System.InvalidCastException e) {
						Console.WriteLine( e.StackTrace);
					}
				}
			} catch( System.IO.IOException e) {
				Console.WriteLine( e.StackTrace);
					return;
			}
			
		}
		
		public static TicketsDB getInstance( ) {
			if ( _instance == null) {
				lock( initLock) {
					if (_instance == null)
						_instance = new TicketsDB( );
				}
			}
			return _instance;
		}

		public IList<Flight> getFlights( ) {
			return flights;
		}
		
		public List<TicketReservation> getFlightReservation( int flightNum) {
			List<TicketReservation> result = null;
			reservations.TryGetValue( flightNum, out result);
			return result;
		}
		
		public Status makeReservation( int flightNum, DateTime date, out TicketReservation finalReservation) {
			finalReservation = null;
			lock( locker) {
				foreach( Flight flight in flights) {
					if ( flight.flightNumber != flightNum || !flight.date.Equals( date)) {
						continue;
					}
					// Check whenever we can there is a free space
					// to make new reservation for desired
					// flight.
					if ( flight.seatsCount == 0) {
						return Status.FLIGHT_FULL;
					}
					// Check if we have already created list of 
					// reservations for given flight. If not
					// then create a new one.
					if ( !reservations.ContainsKey( flightNum)) {
						reservations.Add( flightNum, new List<TicketReservation>( ));
					}
					// Process with reservation.
					List<TicketReservation> flightRsrvs = null;
					if ( reservations.TryGetValue( flightNum, out flightRsrvs)) {
						flight.seatsCount--;
						TicketReservation rsrv = new TicketReservation( flight);
						flightRsrvs.Add( rsrv);
						finalReservation = rsrv;
						return Status.RESERVED_TICKET;
					}
				}
			}
			return Status.RESERVATION_FAILED;
		}
		
		public Status cancelReservation( int reservationId) {
			lock( locker) {
				foreach ( List<TicketReservation> reservs in reservations.Values) {
					foreach( TicketReservation reserv in reservs) {
						if ( reserv.reservationId == reservationId) {
							reservs.Remove( reserv);
							reserv.flight.seatsCount++;
							return Status.RESERVATION_CANCELLED;
						}	
					}
				}
			}
			return Status.CANCELATION_FAILED;
		}		
	}
	
}

